﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
//using MassIntellect.WorkIt;
using System.Reflection;
using MassIntellect.WorkIt;

namespace MassIntellect
{
	//public class XmlSerializeProvider
	//{
	//    /// <summary>
	//    /// Сериализация объекта в XElement
	//    /// </summary>
	//    /// <param name="subj">Объект</param>
	//    /// <returns>Возвращаемый элемент имеет имя 'Element' и атрибут type с наименованием типа (для классов-прототипов имя генерится с треугольными скобками, как в синтаксисе C#).</returns>
	//    public static XElement Serialize(object subj)
	//    {
	//        return Serialize(subj, false);
	//    }

	//    /// <summary>
	//    /// Сериализация объекта в XElement
	//    /// </summary>
	//    /// <param name="subj">Объект</param>
	//    /// <param name="elementName">Имя выходного элемента. Тип будет записан в атрибут type.</param>
	//    /// <returns></returns>
	//    public static XElement Serialize(object subj, string elementName)
	//    {
	//        XElement xe = Serialize(subj, true);
	//        xe.Name = elementName;
	//        return xe;
	//    }

	//    /// <summary>
	//    /// Сериализация объекта в XElement
	//    /// </summary>
	//    /// <param name="subj">Объект</param>
	//    /// <param name="saveTypeNameToAttribute">true - сохранение имени класса в атрибуте type, иначе в имени элемента.</param>
	//    /// <returns>Возвращаемый элемент имеет имя 'Element' и атрибут type с наименованием типа (для классов-прототипов имя генерится с треугольными скобками, как в синтаксисе C#).</returns>
	//    public static XElement Serialize(object subj, bool saveTypeNameToAttribute)
	//    {
	//        ISerialize ser = subj as ISerialize;
	//        string nm = subj.GetType().GetGenericName();
	//        if (nm.Contains('['))
	//            saveTypeNameToAttribute = true;
	//        if (ser != null)
	//        {
	//            XElement xe = ser.Serialize();
	//            if (saveTypeNameToAttribute)
	//                xe.SetAttributeValue("type", nm);
	//            else
	//                xe.Name = xe.Name.Namespace + nm;
	//            return xe;
	//        }
	//        else
	//            return transform(nm, subj, saveTypeNameToAttribute);
	//    }

	//    /// <summary>
	//    /// Сериализация только свойств и типа объекта, без попытки приведения к ISerialize
	//    /// </summary>
	//    /// <param name="subj">Объект</param>
	//    /// <param name="saveTypeNameToAttribute">true - сохранение имени класса в атрибуте type, иначе в имени элемента.</param>
	//    /// <returns></returns>
	//    public static XElement SerializeOnlyPropertyAndType(object subj, bool saveTypeNameToAttribute)
	//    {
	//        return transform(subj.GetType().GetGenericName(), subj, saveTypeNameToAttribute);
	//    }

	//    private static XElement transform(string nameType, object subj, bool saveTypeNameToAttribute)
	//    {
	//        string nm = nameType;
	//        object value;
	//        if (nm.Contains('['))
	//            saveTypeNameToAttribute = true;
	//        var props = subj.GetType().GetPropertiesWithAttribute<SerializeAttribute>();
	//        XElement xe = new XElement(saveTypeNameToAttribute ? "Element" : nm,
	//            saveTypeNameToAttribute ? new XAttribute("type", nm) : null);
	//        foreach (var item in props.Select(s => new KeyValuePair<SerializeAttribute, PropertyInfo>(s.GetAttribute<SerializeAttribute>(), s)))
	//        {
	//            value = item.Value.GetValue(subj, TypeExtension.nullPrms);
	//            if (item.Key.IgnoreDefault || !value.IsDefault())
	//                if (item.Key.SerializeAs == SerializeAs.Attribute)
	//                    xe.Add(new XAttribute(item.Value.Name, value ?? ""));
	//                else if (item.Key.SerializeAs == SerializeAs.Element)
	//                    xe.Add(new XElement(item.Value.Name, value));
	//        }
	//        return xe;
	//    }

	//    /// <summary>
	//    /// Десериализация объекта (класс объекта должен иметь public-конструктор без параметров).
	//    /// Xml-элемент должен содержать атрибут type, в котором указано имя класса (для классов-прототипов имя с треугольными скобками, как в синтаксисе C#), иначе имя класс можно взять из имени элемента.
	//    /// </summary>
	//    /// <typeparam name="T">Класс к которому приводится полученный объект.</typeparam>
	//    /// <param name="xe">Xml-элемент, содержащий сериализованный объект.</param>        
	//    /// <returns>Приведенный объект</returns>
	//    /// <exception cref="Exception">Вызывается в случае неверного имени типа или отсутствия public-конструктора без параметров. В поле Exception.Message описание ошибки.</exception>
	//    public static T Deserialize<T>(XElement xe)
	//        where T : class
	//    {
	//        return (T)Deserialize(xe, typeof(T));
	//    }

	//    /// <summary>
	//    /// Десериализация значения из XElement'а
	//    /// </summary>
	//    /// <typeparam name="T"></typeparam>
	//    /// <param name="xe"></param>
	//    /// <returns></returns>
	//    public static T DeserializeValue<T>(XElement xe)
	//    {
	//        object res;
	//        TryConvert(xe, typeof(T), out res);
	//        return (T)res;
	//    }

	//    /// <summary>
	//    /// Десериализация объекта (класс объекта должен иметь public-конструктор без параметров).
	//    /// Xml-элемент должен содержать атрибут type, в котором указано имя класса (для классов-прототипов имя с треугольными скобками, как в синтаксисе C#), иначе имя класс можно взять из имени элемента.
	//    /// </summary>
	//    /// <typeparam name="T">Класс к которому приводится полученный объект.</typeparam>
	//    /// <param name="xe">Xml-элемент, содержащий сериализованный объект.</param>        
	//    /// <returns>Приведенный объект</returns>
	//    /// <exception cref="Exception">Вызывается в случае неверного имени типа или отсутствия public-конструктора без параметров. В поле Exception.Message описание ошибки.</exception>
	//    public static object Deserialize(XElement xe, params Type[] compatibles)
	//    {
	//        object subj = CreateInstance(xe, compatibles);
	//        ISerialize ser = subj as ISerialize;
	//        if ((subj != null).IfFalse(() => new Exception("Не удалось создать экземпляр типа {0}".goFormat(GetTypeName(xe)))))
	//            if (ser != null)
	//                ser.SetUp(xe);
	//            else
	//                SetUp(subj, xe);
	//        return subj;
	//    }

	//    private static string GetTypeName(XElement xe, bool nameIsElementName)
	//    {
	//        return nameIsElementName ? (string)xe.Attribute("type") : xe.Name.LocalName;
	//    }

	//    private static string GetTypeName(XElement xe)
	//    {
	//        return GetTypeName(xe, xe.Attribute("type") != null);
	//    }

	//    /// <summary>
	//    /// Инстанцирование объекта (класс объекта должен иметь public-конструктор без параметров).
	//    /// Xml-элемент должен содержать атрибут type, в котором указано имя класса (для классов-прототипов имя с треугольными скобками, как в синтаксисе C#), иначе имя класс будет взято из имени элемента.
	//    /// </summary>
	//    /// <typeparam name="T">Класс к которому приводится полученный объект.</typeparam>
	//    /// <param name="xe">Xml-элемент, содержащий сериализованный объект.</param>
	//    /// <returns>Вызывается в случае неверного имени типа или отсутствия public-конструктора без параметров. В поле Exception.Message описание ошибки.</returns>
	//    public static T CreateInstance<T>(XElement xe)
	//        where T : class
	//    {
	//        return CreateInstance<T>(xe, xe.Attribute("type") != null);
	//    }

	//    /// <summary>
	//    /// Инстанцирование объекта (класс объекта должен иметь public-конструктор без параметров).        
	//    /// </summary>
	//    /// <typeparam name="T">Класс к которому приводится полученный объект.</typeparam>
	//    /// <param name="xe">Xml-элемент, содержащий сериализованный объект.</param>
	//    /// <param name="nameIsElementName">true - имя класса будет взято из имени элемента, иначе из атрибута type.</param>
	//    /// <returns>Вызывается в случае неверного имени типа или отсутствия public-конструктора без параметров. В поле Exception.Message описание ошибки.</returns>
	//    public static T CreateInstance<T>(XElement xe, bool nameIsElementName)
	//        where T : class
	//    {
	//        return (T)CreateInstance(xe, nameIsElementName, typeof(T));
	//    }

	//    /// <summary>
	//    /// Инстанцирование объекта (класс объекта должен иметь public-конструктор без параметров).        
	//    /// </summary>
	//    /// <typeparam name="T">Класс к которому приводится полученный объект.</typeparam>
	//    /// <param name="xe">Xml-элемент, содержащий сериализованный объект.</param>
	//    /// <param name="nameIsElementName">true - имя класса будет взято из имени элемента, иначе из атрибута type.</param>
	//    /// <returns>Вызывается в случае неверного имени типа или отсутствия public-конструктора без параметров. В поле Exception.Message описание ошибки.</returns>
	//    public static object CreateInstance(XElement xe, bool nameIsElementName, params Type[] compatibles)
	//    {
	//        Type t = TypeExtension.GetTypeFromNameCompatible(
	//            GetTypeName(xe, nameIsElementName)
	//            , compatibles);
	//        object subj = null;
	//        subj = TypeExtension.CreateInstance(t);
	//        return subj;
	//    }

	//    /// <summary>
	//    /// Инстанцирование объекта (класс объекта должен иметь public-конструктор без параметров).
	//    /// Xml-элемент должен содержать атрибут type, в котором указано имя класса (для классов-прототипов имя с треугольными скобками, как в синтаксисе C#), иначе имя класс будет взято из имени элемента.
	//    /// </summary>
	//    /// <typeparam name="T">Класс к которому приводится полученный объект.</typeparam>
	//    /// <param name="xe">Xml-элемент, содержащий сериализованный объект.</param>
	//    /// <returns>Вызывается в случае неверного имени типа или отсутствия public-конструктора без параметров. В поле Exception.Message описание ошибки.</returns>
	//    public static object CreateInstance(XElement xe, params Type[] compatibles)
	//    {
	//        return CreateInstance(xe, xe.Attribute("type") != null, compatibles);
	//    }

	//    /// <summary>
	//    /// Настройка объекта
	//    /// </summary>
	//    /// <param name="subj">Объект</param>
	//    /// <param name="xe">Xml-элемент, содержащий сериализованный объект (или его часть, например свойства).</param>
	//    public static void SetUp(object subj, XElement xe)
	//    {
	//        XElement x;
	//        object val;
	//        XAttribute xa;
	//        var props = subj.GetType().GetPropertiesWithAttribute<SerializeAttribute>();
	//        foreach (var item in props)
	//            switch (item.GetAttribute<SerializeAttribute>().SerializeAs)
	//            {
	//                case SerializeAs.Element:
	//                    if ((x = xe.Element(item.Name)) != null && TryConvert(x, item.PropertyType, out val))
	//                        item.SetValue(subj, val, TypeExtension.nullPrms);
	//                    break;
	//                case SerializeAs.Attribute:
	//                    if ((xa = xe.Attribute(item.Name)) != null && TryConvert(xa, item.PropertyType, out val))
	//                        item.SetValue(subj, val, TypeExtension.nullPrms);
	//                    break;
	//            }
	//    }

	//    internal static List<Assembly> ScanedAssemblies = new List<Assembly>();
	//    internal static Dictionary<Type, MethodInfo> op_XElementConvert =
	//        new Dictionary<Type, MethodInfo>();
	//    internal static Dictionary<Type, MethodInfo> op_XAttributeConvert =
	//        new Dictionary<Type, MethodInfo>();
	//    internal static Dictionary<Type, MethodInfo> op_TryParse =
	//        new Dictionary<Type, MethodInfo>();

	//    static XmlSerializeProvider()
	//    {
	//        foreach (var item in typeof(XElement).GetMethods().Where(s => s.Name == "op_Explicit" && s.IsSpecialName && s.IsStatic && !op_XElementConvert.ContainsValue(s)))
	//            op_XElementConvert.Add(item.ReturnType, item);

	//        foreach (var item in typeof(XAttribute).GetMethods().Where(s => s.Name == "op_Explicit" && s.IsSpecialName && s.IsStatic && !op_XElementConvert.ContainsValue(s)))
	//            op_XAttributeConvert.Add(item.ReturnType, item);

	//        // При динамической загрузке сборки не учтутся статичные методы TryParse для загруженных типов. Для дозагрузки вызывай UploadTypes()
	//        //UploadTypes();
	//        //op_TryParse.Add
	//    }

	//    ///// <summary>
	//    ///// При динамической загрузке сборки, не учтутся статичные методы TryParse для загруженных типов. Для дозагрузки вызывай UploadTypes() после загрузки сборки. Уже загруженные до этого сборки не проыеряются заново.
	//    ///// </summary>
	//    //public static void UploadTypes()
	//    //{
	//    //    MethodInfo mi;
	//    //    ParameterInfo[] pis;
	//    //    foreach (var item in AppDomain
	//    //                            .CurrentDomain
	//    //                            .GetAssemblies()
	//    //                            .Except(ScanedAssemblies)
	//    //                            .SelectMany(s => s.GetTypes())
	//    //                            .Where(s => s.GetMethods().Any(s1 => s1.IsStatic && s1.Name == "TryParse"))
	//    //                            )
	//    //    {
	//    //        if (!op_TryParse.ContainsKey(item) &&
	//    //            (mi = item
	//    //                .GetMethods(BindingFlags.Public | BindingFlags.Static)
	//    //                .FirstOrDefault(s => s.Name == "TryParse"
	//    //                     && s.ReturnType == typeof(bool)
	//    //                     && (pis = s.GetParameters()).Length == 2
	//    //                     && pis[0].ParameterType == typeof(string)
	//    //                     && pis[1].ParameterType == item.MakeByRefType())) != null)
	//    //            op_TryParse.Add(item, mi);
	//    //    }
	//    //    ScanedAssemblies.AddRange(AppDomain.CurrentDomain.GetAssemblies().Except(ScanedAssemblies));
	//    //}

	//    private static bool tryGetTry_op(Type type, out MethodInfo mi)
	//    {
	//        ParameterInfo[] pis;
	//        if (op_TryParse.TryGetValue(type, out mi))
	//            return true;
	//        else if ((mi = type
	//                    .GetMethods(BindingFlags.Public | BindingFlags.Static)
	//                    .FirstOrDefault(s => s.Name == "TryParse"
	//                         && s.ReturnType == typeof(bool)
	//                         && (pis = s.GetParameters()).Length == 2
	//                         && pis[0].ParameterType == typeof(string)
	//                         && pis[1].ParameterType == type.MakeByRefType())) != null)
	//        {
	//            op_TryParse.Add(type, mi);
	//            return true;
	//        }
	//        else
	//            return false;
	//    }

	//    private static bool tryConvert(Dictionary<Type, MethodInfo> dictMethods, object subj, Func<string> funcSubjToString, Type toType, out object val)
	//    {
	//        MethodInfo mi;
	//        val = null;
	//        bool b;
	//        object[] prms;
	//        if (b = toType.IsEnum)
	//            val = Enum.Parse(toType, funcSubjToString(), true);
	//        else if (b = dictMethods.TryGetValue(toType, out mi))
	//            val = mi.Invoke(null, new object[] { subj });
	//        else if (tryGetTry_op(toType, out mi))
	//        {
	//            b = (bool)mi
	//                .Invoke(null, prms = new object[] { funcSubjToString(), null });
	//            val = prms[1];
	//        }
	//        return b;
	//    }

	//    private static bool TryConvert(XElement xe, Type type, out object val)
	//    {
	//        return tryConvert(op_XElementConvert, xe, () => xe.Value, type, out val);
	//    }

	//    private static bool TryConvert(XAttribute xa, Type type, out object val)
	//    {
	//        return tryConvert(op_XAttributeConvert, xa, () => xa.Value, type, out val);
	//    }
	//}
}
